/*****************************************************************************
 * Copyright (c) 2019, Nations Technologies Inc.
 *
 * All rights reserved.
 * ****************************************************************************
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Nations' name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY NATIONS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL NATIONS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************/

/**
 * @file main.c
 * @author Nations
 * @version v1.0.4
 *
 * @copyright Copyright (c) 2019, Nations Technologies Inc. All rights reserved.
 */

/** @addtogroup
 * @{
 */

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include "nss32lbx.h"
#include "dfu_led.h"
#include "ns_dfu_boot.h"
#include "dfu_crc.h"
#include "dfu_usart.h"
#include "main.h"
#include "crc.h"

#define DEBUG_LOG   1

#if DEBUG_LOG
#define BOOT_PRINT(format, args...) printf(format, ##args);
//#define OTA_HEXDUMP hal_hexdump /* 十六进制打印数组 */
#else
#define OTA_PRINT(...)
//#define OTA_HEXDUMP hal_hexdump_null /* 十六进制打印数组 */
#endif

void ns_log_lpuart_init(void);
uint8_t cpy_data_buffer[FLASH_SECTOR_SIZE] = {0};


void erase_flash_loop(uint32_t addr, uint32_t len)
{
    uint16_t sector_num = (len / FLASH_SECTOR_SIZE);
    uint16_t loop_num;
    uint32_t addr_offset;
    if (len % FLASH_SECTOR_SIZE)
    {
        sector_num += 1;
    }

    for (loop_num = 0; loop_num < sector_num; loop_num++)
    {
        addr_offset = addr + (loop_num * FLASH_SECTOR_SIZE);
        Qflash_Erase_Sector(addr_offset);
    }
}

void cpy_new_fw_loop(uint32_t target_start_addr, uint32_t data_addr, uint32_t len)
{
    uint16_t loop_num;
    uint32_t add_offset, read_addr, write_addr;
    uint32_t sector_count         = len / FLASH_SECTOR_SIZE;
    uint16_t sector_remainder_len = len % FLASH_SECTOR_SIZE;

    for (loop_num = 0; loop_num < sector_count; loop_num++)
    {
        add_offset = loop_num * FLASH_SECTOR_SIZE;
        read_addr  = data_addr + add_offset;
        write_addr = target_start_addr + add_offset;
        Qflash_Read(read_addr, cpy_data_buffer, FLASH_SECTOR_SIZE);
        Qflash_Write(write_addr, cpy_data_buffer, FLASH_SECTOR_SIZE);
    }

    if (sector_remainder_len)
    {
        add_offset = loop_num * FLASH_SECTOR_SIZE;
        read_addr  = data_addr + add_offset;
        write_addr = target_start_addr + add_offset;
        Qflash_Read(read_addr, cpy_data_buffer, sector_remainder_len);
        Qflash_Write(write_addr, cpy_data_buffer, sector_remainder_len);
    }
}

uint32_t soft_crc32_caculate(uint32_t data_addr, uint32_t len)
{
    uint16_t loop_num;
    uint32_t add_offset, read_addr,new_bin_crc32;
    uint32_t sector_count         = len / FLASH_SECTOR_SIZE;
    uint16_t sector_remainder_len = len % FLASH_SECTOR_SIZE;

	crc32_init(0x04C11DB7);
	new_bin_crc32=crc32_get_init_value();
	
    for (loop_num = 0; loop_num < sector_count; loop_num++)
    {
        add_offset = loop_num * FLASH_SECTOR_SIZE;
        read_addr  = data_addr + add_offset;
        Qflash_Read(read_addr, cpy_data_buffer, FLASH_SECTOR_SIZE);
		new_bin_crc32=crc32_calc_part_data(new_bin_crc32,cpy_data_buffer,FLASH_SECTOR_SIZE);
    }

    if (sector_remainder_len)
    {
        add_offset = loop_num * FLASH_SECTOR_SIZE;
        read_addr  = data_addr + add_offset;
        Qflash_Read(read_addr, cpy_data_buffer, sector_remainder_len);
		new_bin_crc32=crc32_calc_part_data(new_bin_crc32,cpy_data_buffer,sector_remainder_len);

    }
	new_bin_crc32=crc32_calc_final(new_bin_crc32);
	
	return new_bin_crc32;
    
}

#ifdef TY_BLE_ENABLE // 涂鸦OTA
void cpy_new_fw_to_app1(void)
{
    uint32_t new_bin_crc32          = 0;
    tkl_ota_fw_info_t new_fw_info   = {0};
    NS_Bootsetting_t bootseting_tmp = {0};
    Qflash_Init();

    Qflash_Read(FLASH_OTA_BIN_INFO_BASE_ADDR, (uint8_t *)&new_fw_info, sizeof(tkl_ota_fw_info_t));/* 读取新固件信息 */
//	BOOT_PRINT("new_bin_transfer_complete=0x%X\n",new_fw_info.new_bin_transfer_complete);
    if (new_fw_info.new_bin_transfer_complete == 0xaa)/* 新固件传输完成 */
    {
		BOOT_PRINT("new version crc caculate\n");
		new_bin_crc32 = soft_crc32_caculate(FLASH_OTA_BACKUP_BASE_ADDR, new_fw_info.total_bin_len);/* 计算新固件校验 */
		BOOT_PRINT("new_bin_crc32=0x%X,new_fw_info=0x%X\n",new_bin_crc32,new_fw_info.ota_fw_ns_crc32);
		if(new_bin_crc32==new_fw_info.ota_fw_ns_crc32)
		{
			BOOT_PRINT("erase\n");
			erase_flash_loop(NS_APP1_START_ADDRESS, new_fw_info.total_bin_len);/* 檫除APP区域 */
			BOOT_PRINT("erase ok\n");
			cpy_new_fw_loop(NS_APP1_START_ADDRESS, FLASH_OTA_BACKUP_BASE_ADDR, new_fw_info.total_bin_len);/* 复制新固件进APP区域 */
			BOOT_PRINT("copy ok\n");
		}
#if 0        
		else
		{
			BOOT_PRINT("crc value error!\n");
		}

		/* 复位固件信息数据 */
		memset(&new_fw_info, 0, sizeof(tkl_ota_fw_info_t));
		new_fw_info.new_bin_updata_complete=0x5A;/* 通知APP */
		Qflash_Erase_Sector(FLASH_OTA_BIN_INFO_BASE_ADDR);
		Qflash_Write(FLASH_OTA_BIN_INFO_BASE_ADDR, (uint8_t *)&new_fw_info, sizeof(tkl_ota_fw_info_t));
#else
		else
		{
			BOOT_PRINT("crc value error!\n");
            new_fw_info.new_bin_updata_complete=0xA8;
		}
        
		/* 复位固件信息数据 */
        if(new_fw_info.new_bin_updata_complete == 0xA8)
        {
            memset(&new_fw_info, 0, sizeof(tkl_ota_fw_info_t));
            new_fw_info.new_bin_updata_complete=0xA8;/* 通知APP */                
        }
        else
        {
            memset(&new_fw_info, 0, sizeof(tkl_ota_fw_info_t));
            new_fw_info.new_bin_updata_complete=0x5A;/* 通知APP */            
        }

		Qflash_Erase_Sector(FLASH_OTA_BIN_INFO_BASE_ADDR);
		Qflash_Write(FLASH_OTA_BIN_INFO_BASE_ADDR, (uint8_t *)&new_fw_info, sizeof(tkl_ota_fw_info_t));
#endif		
//        new_bin_crc32 = dfu_crc32(((uint8_t *)(uint32_t *)NS_APP1_START_ADDRESS), new_fw_info.total_bin_len);/* 计算新固件校验 */
//        if (new_bin_crc32 == new_fw_info.ota_fw_ns_crc32) //检查写入的新固件校验值是否跟记录的校验一致
//        {
//            //BOOT_PRINT("check crc success after cpy11\r\n");
//            //BOOT_PRINT("seting add=%x\r\n", (uint32_t )&(ns_bootsetting.app1.start_address));
//            memcpy((uint8_t *)&bootseting_tmp, (uint8_t *)&ns_bootsetting, sizeof(NS_Bootsetting_t));
//            bootseting_tmp.app1.activation          = NS_BOOTSETTING_ACTIVATION_NO;
//            bootseting_tmp.app2.activation          = NS_BOOTSETTING_ACTIVATION_NO;
//            bootseting_tmp.ImageUpdate.activation   = NS_BOOTSETTING_ACTIVATION_NO;
//            bootseting_tmp.master_boot_force_update = NS_BOOTSETTING_MASTER_BOOT_FORCE_UPDATE_NO;

//            bootseting_tmp.app1.start_address = NS_APP1_START_ADDRESS;
//            bootseting_tmp.app1.size          = new_fw_info.total_bin_len;
//            bootseting_tmp.app1.version       = 0;
//            bootseting_tmp.app1.crc           = new_bin_crc32;
//            bootseting_tmp.app1.activation    = NS_BOOTSETTING_ACTIVATION_YES;

//            bootseting_tmp.crc = dfu_crc32((uint8_t *)&ns_bootsetting.crc + 4, sizeof(NS_Bootsetting_t) - 4);
//            #if (DEBUG_LOG == 1)
//            BOOT_PRINT("check crc success after cpy1\r\n");
//            #endif
//            Qflash_Erase_Sector(NS_BOOTSETTING_START_ADDRESS);
//            Qflash_Write(NS_BOOTSETTING_START_ADDRESS, (uint8_t *)&bootseting_tmp, sizeof(NS_Bootsetting_t));
//            //BOOT_PRINT("check crc success after cpy2\r\n");
//            if (bootseting_tmp.crc
//                == dfu_crc32((uint8_t *)((uint32_t *)(NS_BOOTSETTING_START_ADDRESS + 4)), sizeof(NS_Bootsetting_t) - 4))
//            {
//                #if (DEBUG_LOG == 1)
//                BOOT_PRINT("check crc success after write bootseting\r\n");
//                #endif
//                memset(&new_fw_info, 0, sizeof(tkl_ota_fw_info_t));
//                Qflash_Erase_Sector(FLASH_OTA_BIN_INFO_BASE_ADDR);
//                Qflash_Write(FLASH_OTA_BIN_INFO_BASE_ADDR, (uint8_t *)&new_fw_info, sizeof(tkl_ota_fw_info_t));
//            }
//            else
//            {
//                #if (DEBUG_LOG == 1)
//                BOOT_PRINT("check crc failed after write bootseting\r\n");
//                #endif
//                RCC_ClrFlag();
//                NVIC_SystemReset();
//            }
//        }
//        else/* 校验失败，复位重走写入校验流程 */
//        {
//            #if (DEBUG_LOG == 1)
//            BOOT_PRINT("check crc failed after cpy\r\n");
//            #endif
//            RCC_ClrFlag();
//            NVIC_SystemReset();
//        }
    }
}
#else
void cpy_new_fw_to_app1(void)
{
    uint32_t new_bin_crc32          = 0;
    new_bin_info_t new_fw_info      = {0};
    NS_Bootsetting_t bootseting_tmp = {0};

    Qflash_Init();
    Qflash_Read(NEW_BIN_BACKUP_ADDR, (uint8_t *)&new_fw_info, sizeof(new_fw_info));
    BOOT_PRINT("crc=0x%x\r\n", new_fw_info.crc);
    BOOT_PRINT("to_be_copied_flag=0x%x\r\n", new_fw_info.to_be_copied_flag);
    BOOT_PRINT("app_crc=0x%x\r\n", new_fw_info.app_crc);
    BOOT_PRINT("app_size=0x%x\r\n", new_fw_info.app_size);
    if (new_fw_info.to_be_copied_flag == 0xaa)
    {
        if (new_fw_info.crc != dfu_crc32((uint8_t *)&new_fw_info + 4, sizeof(new_fw_info) - 4))
        {
            BOOT_PRINT("crc err\r\n");
            Qflash_Erase_Sector(NEW_BIN_BACKUP_ADDR);
            return;
        }

        erase_flash_loop(NS_APP1_START_ADDRESS, new_fw_info.app_size);
        cpy_new_fw_loop(NS_APP1_START_ADDRESS, NEW_BIN_BACKUP_ADDR + FLASH_SECTOR_SIZE, new_fw_info.app_size);

        new_bin_crc32 = dfu_crc32(((uint8_t *)(uint32_t *)NS_APP1_START_ADDRESS), new_fw_info.app_size);
        if (new_bin_crc32 == new_fw_info.app_crc) //check crc32 of new bin after copy.
        {
            memcpy((uint8_t *)&bootseting_tmp, (uint8_t *)&ns_bootsetting, sizeof(NS_Bootsetting_t));
            bootseting_tmp.app1.activation          = NS_BOOTSETTING_ACTIVATION_NO;
            bootseting_tmp.app2.activation          = NS_BOOTSETTING_ACTIVATION_NO;
            bootseting_tmp.ImageUpdate.activation   = NS_BOOTSETTING_ACTIVATION_NO;
            bootseting_tmp.master_boot_force_update = NS_BOOTSETTING_MASTER_BOOT_FORCE_UPDATE_NO;

            bootseting_tmp.app1.start_address = NS_APP1_START_ADDRESS;
            bootseting_tmp.app1.size          = new_fw_info.app_size;
            bootseting_tmp.app1.version       = 0;
            bootseting_tmp.app1.crc           = new_bin_crc32;
            bootseting_tmp.app1.activation    = NS_BOOTSETTING_ACTIVATION_YES;

            bootseting_tmp.crc = dfu_crc32((uint8_t *)&ns_bootsetting.crc + 4, sizeof(NS_Bootsetting_t) - 4);

            Qflash_Erase_Sector(NS_BOOTSETTING_START_ADDRESS);
            Qflash_Write(NS_BOOTSETTING_START_ADDRESS, (uint8_t *)&bootseting_tmp, sizeof(NS_Bootsetting_t));

            if (bootseting_tmp.crc
                == dfu_crc32((uint8_t *)((uint32_t *)(NS_BOOTSETTING_START_ADDRESS + 4)), sizeof(NS_Bootsetting_t) - 4))
            {
                new_fw_info.to_be_copied_flag = 0;
                Qflash_Erase_Sector(NEW_BIN_BACKUP_ADDR);
                Qflash_Write(NEW_BIN_BACKUP_ADDR, (uint8_t *)&new_fw_info, sizeof(new_fw_info));
            }
            else
            {
                BOOT_PRINT("dfu crc err\r\n");
                RCC_ClrFlag();
                NVIC_SystemReset();
            }
        }
        else
        {
            BOOT_PRINT("app crc err\r\n");
            RCC_ClrFlag();
            NVIC_SystemReset();
        }
    }
}
#endif

/**
 * @brief Loop up the bootsetting and decide which bank to jump to , or stay in masterboot for usart image update.
 * @param[in] none.
 * @return none
 */
static void masterboot(void)
{
    uint32_t jump_address = NULL;

//    BOOT_PRINT("crc=0x%x,force=0x%x\r\n", ns_bootsetting.crc, ns_bootsetting.master_boot_force_update);
//    BOOT_PRINT("app1.start_address=0x%x\r\n", ns_bootsetting.app1.start_address);
//    BOOT_PRINT("app1.crc=0x%x\r\n", ns_bootsetting.app1.crc);
//    BOOT_PRINT("app1.size=0x%x\r\n", ns_bootsetting.app1.size);
//    BOOT_PRINT("app1.version=0x%x\r\n", ns_bootsetting.app1.version);
//    BOOT_PRINT("app1.activation=0x%x\r\n", ns_bootsetting.app1.activation);
	BOOT_PRINT("enter app\r\n");

//    if (ns_bootsetting.crc == dfu_crc32((uint8_t *)&ns_bootsetting.crc + 4, sizeof(NS_Bootsetting_t) - 4))
//    {
//        if (ns_bootsetting.master_boot_force_update != NS_BOOTSETTING_MASTER_BOOT_FORCE_UPDATE_YES)
//        {
//            if (ns_bootsetting.app1.activation == NS_BOOTSETTING_ACTIVATION_YES
//                && ns_bootsetting.app1.start_address == NS_APP1_START_ADDRESS)
//            {
//                if (ns_bootsetting.app1.crc
//                    == dfu_crc32((uint8_t *)((uint32_t *)ns_bootsetting.app1.start_address), ns_bootsetting.app1.size))
//                {
//                    jump_address = ns_bootsetting.app1.start_address;
//                }
//            }
//            else
//            {
//                if (ns_bootsetting.app1.crc
//                    == dfu_crc32((uint8_t *)((uint32_t *)ns_bootsetting.app1.start_address), ns_bootsetting.app1.size))
//                {
//                    jump_address = ns_bootsetting.app1.start_address;
//                }
//            }
//        }
//    }
//    else
//    {
//        if ((ns_bootsetting.crc == 0xFFFFFFFF)
//            && (ns_bootsetting.master_boot_force_update == 0xFFFFFFFF)) // 判断为空，默认跳转APP1
//        {
//            jump_address = NS_APP1_START_ADDRESS;
//        }
//    }

    jump_address = NS_APP1_START_ADDRESS; // for test

    if (jump_address != NULL)
    {
        dfu_usart1_default_config();
        ns_dfu_boot_jump(jump_address);
    }
}

/**
 * @brief  main function
 * @param
 * @return
 * @note   Note
 */
int main(void)
{
    #if (DEBUG_LOG == 1)
    ns_log_lpuart_init();
    BOOT_PRINT("power on\r\n");
    #endif

    cpy_new_fw_to_app1();
    masterboot();

    while (1)
    {
    }
}

void RCC_Configuration(uint32_t LPUART_CLK_SRC)
{
    switch (LPUART_CLK_SRC)
    {
        case RCC_LPUARTCLK_SRC_LSI_LSE:
        {
            /* Specifies the LPUARTx clock source, LSE selected as LPUARTx clock */
            RCC_ConfigLpuartClk(RCC_LPUART1CLK, RCC_LPUARTCLK_SRC_LSI_LSE);
        }
        break;
        default:
        {
            /* Specifies the LPUARTx clock source, APB1 selected as LPUARTx clock */
            RCC_ConfigLpuartClk(RCC_LPUART1CLK, RCC_LPUARTCLK_SRC_APB1);
        }
        break;
    }

    /* Enable LPUARTx Clock */
    RCC_EnableLpuartClk(ENABLE);
}

void GPIO_Configuration(void)
{
    GPIO_InitType GPIO_InitStructure;

    /* Enable GPIO clock */
    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_AFIO, ENABLE);

    /* Initialize GPIO_InitStructure */
    GPIO_InitStruct(&GPIO_InitStructure);
    /* Configure LPUARTx Tx as alternate function push-pull */
    GPIO_InitStructure.Pin            = GPIO_PIN_1;
    GPIO_InitStructure.GPIO_Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF4_LPUART1;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);
}

void ns_log_lpuart_init(void)
{
    LPUART_InitType LPUART_InitStructure;

    /* System Clocks Configuration */
    RCC_Configuration(RCC_LPUARTCLK_SRC_APB1);

    /* Configure the GPIO ports */
    GPIO_Configuration();

    LPUART_DeInit(LPUART1);
    /* LPUARTx configuration ------------------------------------------------------*/
    LPUART_InitStructure.BaudRate            = 115200;
    LPUART_InitStructure.Parity              = LPUART_PE_NO;
    LPUART_InitStructure.RtsThreshold        = LPUART_RTSTH_FIFOFU;
    LPUART_InitStructure.HardwareFlowControl = LPUART_HFCTRL_NONE;
    LPUART_InitStructure.Mode                = LPUART_MODE_TX;
    /* Configure LPUARTx */
    LPUART_Init(LPUART1, &LPUART_InitStructure);
}

int fputc(int ch, FILE *f)
{
    static uint8_t func_lock = 1;
    if (func_lock)
    {
        func_lock = 0;
        LPUART_SendData(LPUART1, (uint8_t)ch);
        while (LPUART_GetFlagStatus(LPUART1, LPUART_FLAG_TXC) == RESET)
            ;
        LPUART_ClrFlag(LPUART1, LPUART_FLAG_TXC);
        func_lock = 1;
    }

    return (ch);
}
